#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include "tagfs.h"

// recursively remove non empty directory
// adapt from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
int remove_directory(const char *path)
{
	DIR *dir = opendir(path);
	size_t path_len = strlen(path);
	int res;

	if(dir)
	{
		struct dirent *entry;

		res = 0;

		while (!res && (entry=readdir(dir)))
		{
			int res2 = -1;
			char *buf;
			size_t len;

			/* Skip the names "." and ".." as we don't want to recurse on them. */
			if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
			{
				continue;
			}

			len = path_len + strlen(entry->d_name) + 2; 
			buf = malloc(len);

			if (buf)
			{
				struct stat statbuf;
				snprintf(buf, len, "%s/%s", path, entry->d_name);
				if (!stat(buf, &statbuf))
				{
					if (S_ISDIR(statbuf.st_mode))
					{
						res2 = remove_directory(buf);
					}
					else
					{
						res2 = unlink(buf);
					}
				}
				free(buf);
			}
			res = res2;
		}
		closedir(dir);
	}
	else
	{
		return 0; // No such directory is ok
	}

	if (!res)
	{
		res = rmdir(path);
	}

	return res;
}

// return 0 on success
int get_tagid(char* tag, unsigned int* tagid) 
{
	FILE* fp;
	char system_filepath[256];
	struct tagid_name entry;
	get_system_filepath("/.tagid_tagname", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		return -1;
	while(!feof(fp))
	{
		fread(&entry, sizeof(struct tagid_name), 1, fp);
		if(!strcmp(tag, entry.name))
		{
			fclose(fp);
			*tagid = entry.id;
			return 0;
		}
	}
	fclose(fp);
	return -1;
}

int get_num_inode(unsigned int tagid)
{
	FILE* fp;
	char system_filepath[256];
	unsigned int size, entry;
	get_system_filepath("/.tagid_inode", system_filepath);
	fp = fopen(system_filepath, "rb");
	if(fp == NULL)
		return -1;
	do
	{
		fread(&size, sizeof(unsigned int), 1, fp);
		fread(&entry, sizeof(unsigned int), 1, fp);
		if(entry == tagid)
		{
			fclose(fp);
			return size-2;
		}
	} while(!feof(fp) && !fseek(fp, sizeof(unsigned int) * (size-2), SEEK_CUR));
	fclose(fp);
	return -1;
}

void get_inode_from_tagid(unsigned int tagid, unsigned int* inode)
{
	FILE* fp;
	char system_filepath[256];
	unsigned int size, entry;
	int i;
	get_system_filepath("/.tagid_inode", system_filepath);
	fp = fopen(system_filepath, "rb");
	do
	{
		fread(&size, sizeof(unsigned int), 1, fp);
		fread(&entry, sizeof(unsigned int), 1, fp);
		if(entry == tagid)
		{
			for(i=0; i<size-2; i++)
			{
				fread(&inode[i], sizeof(unsigned int), 1, fp);
			}
			fclose(fp);
			return;
		}
	} while(!feof(fp) && !fseek(fp, sizeof(unsigned int) * (size-2), SEEK_CUR));
	fclose(fp);
}

void get_file_list(unsigned int* inode, char** file_list, int num_inode)
{
	FILE* fp;
	struct inode_name entry;
	char system_filepath[256];
	int i;
	get_system_filepath("/.inode_name", system_filepath);
	for(i=0; i<num_inode; i++)
	{
		fp = fopen(system_filepath, "rb");
		while(!feof(fp))
		{
			fread(&entry, sizeof(struct inode_name), 1, fp);
			if(entry.inode == inode[i])
			{
				strcpy(file_list[i], entry.name);
			}
		}
		fclose(fp);
	}

}

int main(int argc, char *argv[])
{
	FILE* fp;
	int i, j, k, tmp;
	int num_inode;
	int num_tag;
	int res;
	int a_value = 0; 
	int o_value = 0;
	char* filename;
	char path[256];
	char** tag;
	char** file_list;
	unsigned int* inode_res;
	unsigned int* inode;
	unsigned int* tagid;

	// handle inputs
	if(argc < 2)
	{
		printf("invalid argument\n");
		return 1;
	}
	else if(!strcmp("-o", argv[1]))
	{
		o_value = 1;
		num_tag = argc-2;
		tag = (char**)malloc(num_tag*sizeof(char*));
		for(i=0; i<num_tag; i++)
		{
			tag[i] = argv[i+2];
		}
	}
	else if(!strcmp("-a", argv[1]))
	{
		a_value = 1;
		num_tag = argc-2;
		tag = (char**)malloc(num_tag*sizeof(char*));
		for(i=0; i<num_tag; i++)
		{
			tag[i] = argv[i+2];
		}
	}
	else
	{
		num_tag = argc-1;
		if(num_tag > 1)
		{
			printf("invalid argument\n");
			return 1;
		}
		tag = (char**)malloc(num_tag*sizeof(char*));
		tag[0] = argv[1];
	}

	// get tagid list
	tagid = (unsigned int*)malloc(num_tag*sizeof(unsigned int));
	tmp = num_tag;
	num_tag = 0;
	for(i=0; i<tmp; i++)
	{
		res = get_tagid(tag[num_tag], &tagid[num_tag]);
		if(!res)
		{
			num_tag++;
		}
	}
	// there is no such tag (all of the tag input are not valid)
	if(!num_tag)
	{
		printf("No search result\n");
		return 1;
	}

	// construct the result from each tag
	num_inode = 0;	
	for(i=0; i<num_tag; i++)
	{
		num_inode += get_num_inode(tagid[i]);	
	}
	inode_res = (unsigned int*)malloc(num_inode*sizeof(unsigned int));
	num_inode = 0;
	for(i=0; i<num_tag; i++)
	{
		tmp = get_num_inode(tagid[i]);	
		// allocate inode
		inode = (unsigned int*)malloc(tmp*sizeof(unsigned int));
		// list every tagid
		get_inode_from_tagid(tagid[i], inode);
		// add inode to the result considering the option
		if(num_inode == 0)
		{
			// fill first result result
			for(j=0; j<tmp; j++)
			{
				inode_res[j] = inode[j];
			}
			num_inode = tmp;
		}
		else
		{
			if(o_value) // or option
			{
				for(j=0; j<tmp; j++)
				{
					for(k=0; k<num_inode; k++)
					{
						if(inode[j] == inode_res[k])
							break;
					}
					if(k == num_inode) // new inode will be added
					{
						inode_res[num_inode++] = inode[j];
					}
				}
			}
			else if(a_value) // and option
			{
				for(k=0; k<num_inode; k++)
				{
					for(j=0; j<tmp; j++)
					{
						if(inode[j] == inode_res[k])
							break;
					}
					if(j == tmp) // inode don't match -> remove it
					{
						inode_res[k] = inode_res[num_inode--];
					}
				}
			}
		}
	}
	// the tag has no corresponding inode
	if(num_inode == 0)
	{
		printf("No search result\n");
		return 1;
	}
	// allocate file_list
	file_list = (char**)malloc(num_inode*sizeof(char*));
	for(i=0; i<num_inode; i++)
		file_list[i] = (char*)malloc(256*sizeof(char));
	// convert inode to file path
	get_file_list(inode_res, file_list, num_inode);

	// print the result
	/*for(i=0; i<num_inode; i++)
	{
		printf("%s\n", file_list[i]);
	}*/
	
	// create a folder with symbolic link
	// first remove the result directory
	res = remove_directory("searchtag_result");
	if(res)
	{
		printf("Fail to create result\n");
		return 1;
	}
	// recreate the result directory
	mkdir("searchtag_result", 0777);
	// create symbolic link from file name list
	for(i=0; i<num_inode; i++)
	{
		strcpy(path, "searchtag_result/");
		symlink(file_list[i], strcat(path, strrchr(file_list[i], '/')));
	}
	printf("Result is generated in searchtag_result directory\n");
	return 1;
}
